import { Byte } from '../../api/io/Byte';
import { HashMap } from '../../api/struct/HashMap';
import { LegionBossRankReward } from './LegionBossRankReward';
import { ITimer } from '../../api/timer/ITimer';
import { IAsync } from '../../api/event/IAsync';
import { MathUtils } from '../../api/math/MathUtils';



/**军团boss属性枚举*/
export const enum E_LegionBoss {
	/**今日挑战次数*/
	FIGHT_TIMES = 0,
	/**今日历史最大伤害*/
	MAX_SCORE = 1,
	/**当前记录的开始时间戳（本地时间每日0点）*/
	TIME_STAMP = 2,
	/**历史分数*/
	HISTORY_SCORE = 3,
	/**排名奖励<时间，奖励内容>*/
	RANK_REWARD = 4,
	/**历史上打军团boss参与排行过*/
	RANK_JOINED = 5,
	/**历史最高名次 0-没用参与过，>= 1名次*/
	MAX_RANK_INDEX = 6,
	/**枚举数量*/
	EnumCount = 7
}


/**军团boss*/
export class LegionBoss {
	/**事件ID*/
	public static readonly EVENT_ID = 'STRUCT_DATA_LEGION_BOSS_EVENT_ID';
	/**整体更新事件ID（部分只需要监听整体变化）*/
	public static readonly TOTAL_EVENT_ID = 'STRUCT_DATA_LEGION_BOSS_TOTAL_EVENT_ID';
	/**缓存池*/
	private static POOL: LegionBoss[] = [];

	/**创建入口*/
	public static create(json?: string): LegionBoss {
		const c = this.POOL.pop() || new LegionBoss();
		c._destroyed = false;
		c.resetProperty();
		if (json) {
			c.parse(json);
		}
		return c;
	}


	/**已释放*/
	protected _destroyed = false;
	/**今日挑战次数 (uint16)*/
	protected _fightTimes: number;
	/**今日历史最大伤害 (long)*/
	protected _maxScore: number;
	/**当前记录的开始时间戳（本地时间每日0点） (long)*/
	protected _timeStamp: number;
	/**历史分数 (long)*/
	protected _historyScore: number;
	/**排名奖励<时间，奖励内容>*/
	protected _rankReward: HashMap<number, LegionBossRankReward> = null;
	/**历史上打军团boss参与排行过*/
	protected _rankJoined: boolean;
	/**历史最高名次 0-没用参与过，>= 1名次 (uint32)*/
	protected _maxRankIndex: number;


	/**构建函数-私有，防止外部创建*/
	private constructor() {
	}


	/**释放*/
	public destroy(): void {
		if (this._destroyed) {
			return;
		}
		this._destroyed = true;
		this.resetProperty(true);
		if (LegionBoss.POOL.indexOf(this) === -1) {
			LegionBoss.POOL.push(this);
		}
	}

	/**今日挑战次数*/
	public get fightTimes() {
		return this._fightTimes;
	}


	/**今日挑战次数*/
	public set fightTimes(v: number) {
		this._fightTimes = v;
	}


	/**今日历史最大伤害*/
	public get maxScore() {
		return this._maxScore;
	}


	/**今日历史最大伤害*/
	public set maxScore(v: number) {
		this._maxScore = v;
	}


	/**当前记录的开始时间戳（本地时间每日0点）*/
	public get timeStamp() {
		return this._timeStamp;
	}


	/**当前记录的开始时间戳（本地时间每日0点）*/
	public set timeStamp(v: number) {
		this._timeStamp = v;
	}


	/**历史分数*/
	public get historyScore() {
		return this._historyScore;
	}


	/**历史分数*/
	public set historyScore(v: number) {
		this._historyScore = v;
	}


	/**设置排名奖励<时间，奖励内容>*/
	public putRankReward(k: number, v: LegionBossRankReward): LegionBossRankReward {
		const ov = this._rankReward.put(k, v);
		return ov;
	}


	/**获取排名奖励<时间，奖励内容>*/
	public getRankReward(k: number) {
		return this._rankReward.get(k);
	}


	/**删除排名奖励<时间，奖励内容>*/
	public removeRankReward(k: number, autoDestroy = false): LegionBossRankReward {
		const ov = this._rankReward.remove(k);
		if (autoDestroy && ov) {
			ov.destroy();
		}
		return ov;
	}


	/**获取排名奖励<时间，奖励内容>映射表-键值(外部改变将导致出错，只能用于遍历)*/
	public getRankRewardKeys(): number[] {
		return this._rankReward.getKeys();
	}


	/**获取排名奖励<时间，奖励内容>映射表-值(外部改变将导致出错，只能用于遍历)*/
	public getRankRewardValues(): LegionBossRankReward[] {
		return this._rankReward.getValues();
	}


	/**清空排名奖励<时间，奖励内容>映射表*/
	public clearRankRewardMap(autoDestroy = false): void {
		const ks = this._rankReward.getKeys();
		for (let i = ks.length - 1; i >= 0; i--) {
			this.removeRankReward(ks[i], autoDestroy);
		}
	}


	/**历史上打军团boss参与排行过*/
	public get rankJoined() {
		return this._rankJoined;
	}


	/**历史上打军团boss参与排行过*/
	public set rankJoined(v: boolean) {
		this._rankJoined = v;
	}


	/**历史最高名次 0-没用参与过，>= 1名次*/
	public get maxRankIndex() {
		return this._maxRankIndex;
	}


	/**历史最高名次 0-没用参与过，>= 1名次*/
	public set maxRankIndex(v: number) {
		this._maxRankIndex = v;
	}



	/**通过枚举获取值*/
	public getV(k: E_LegionBoss): any {
		switch (k) {
			case E_LegionBoss.FIGHT_TIMES: return this._fightTimes;
			case E_LegionBoss.MAX_SCORE: return this._maxScore;
			case E_LegionBoss.TIME_STAMP: return this._timeStamp;
			case E_LegionBoss.HISTORY_SCORE: return this._historyScore;
			case E_LegionBoss.RANK_REWARD: return this._rankReward;
			case E_LegionBoss.RANK_JOINED: return this._rankJoined;
			case E_LegionBoss.MAX_RANK_INDEX: return this._maxRankIndex;
			default: return null;
		}
	}


	/**通过枚举设置值*/
	public setV(k: E_LegionBoss, v: any): void {
		switch (k) {
			case E_LegionBoss.FIGHT_TIMES: this._fightTimes = v; break;
			case E_LegionBoss.MAX_SCORE: this._maxScore = v; break;
			case E_LegionBoss.TIME_STAMP: this._timeStamp = v; break;
			case E_LegionBoss.HISTORY_SCORE: this._historyScore = v; break;
			case E_LegionBoss.RANK_REWARD: this._rankReward = v; break;
			case E_LegionBoss.RANK_JOINED: this._rankJoined = v; break;
			case E_LegionBoss.MAX_RANK_INDEX: this._maxRankIndex = v; break;
			default: break;
		}
	}


	/**初始化*/
	public resetProperty(fromDestroy = false): void {
		//今日挑战次数
		this._fightTimes = 0;
		//今日历史最大伤害
		this._maxScore = 0;
		//当前记录的开始时间戳（本地时间每日0点）
		this._timeStamp = 0;
		//历史分数
		this._historyScore = 0;
		//排名奖励<时间，奖励内容>
		if (this._rankReward) {
			const list = this._rankReward.getValues();
			for (let i = 0, len = list.length; i < len; i++) {
				list[i].destroy();
			}
			this._rankReward.clear();
		}
		else {
			this._rankReward = new HashMap<number, LegionBossRankReward>();
		}
		//历史上打军团boss参与排行过
		this._rankJoined = false;
		//历史最高名次 0-没用参与过，>= 1名次
		this._maxRankIndex = 0;
	}


	/**序列化*/
	public stringify(toString: boolean): string | any {
		const json: any = {};
		if (this._fightTimes) {
			json.fightTimes = this._fightTimes;
		}
		if (this._maxScore) {
			json.maxScore = this._maxScore;
		}
		if (this._timeStamp) {
			json.timeStamp = this._timeStamp;
		}
		if (this._historyScore) {
			json.historyScore = this._historyScore;
		}
		if (this._rankReward.size() > 0) {
			const ks = this._rankReward.getKeys();
			const vs = this._rankReward.getValues();
			const list: string[] = [];
			for (let i = 0, len = vs.length; i < len; i++) {
				list[i] = vs[i].stringify(false);
			}
			json.rankReward = [ks, list];
		}
		if (this._rankJoined) {
			json.rankJoined = this._rankJoined;
		}
		if (this._maxRankIndex) {
			json.maxRankIndex = this._maxRankIndex;
		}

		return toString ? JSON.stringify(json) : json;
	}


	/**反序列化*/
	public parse(jsonv: string | any): this {
		this.resetProperty();
		if (!jsonv) {
			return this;
		}
		const json = typeof (jsonv) === 'string' ? JSON.parse(jsonv) : jsonv;
		if (json.fightTimes) {
			this._fightTimes = json.fightTimes;
		}
		if (json.maxScore) {
			this._maxScore = json.maxScore;
		}
		if (json.timeStamp) {
			this._timeStamp = json.timeStamp;
		}
		if (json.historyScore) {
			this._historyScore = json.historyScore;
		}
		if (json.rankReward) {
			const ks: number[] = json.rankReward[0] || (json.rankReward.getKeys ? json.rankReward.getKeys() : null);	//对象可能是json或者结构体
			const vs: string[] = json.rankReward[1] || (json.rankReward.getValues ? json.rankReward.getValues() : null);
			if (ks && vs && ks.length === vs.length) {
				for (let i = 0, len = vs.length; i < len; i++) {
					this._rankReward.put(ks[i], LegionBossRankReward.create(vs[i]));
				}
			}
		}
		if (json.rankJoined) {
			this._rankJoined = json.rankJoined;
		}
		if (json.maxRankIndex) {
			this._maxRankIndex = json.maxRankIndex;
		}
		return this;
	}


	/**序列化-二进制*/
	public toBytes(byte: Byte): void {
		if (!byte) {
			return;
		}
		byte.writeUint16(this._fightTimes);
		byte.writeLongNumber(this._maxScore);
		byte.writeLongNumber(this._timeStamp);
		byte.writeLongNumber(this._historyScore);
		//排名奖励<时间，奖励内容>
		{
			const ks = this._rankReward.getKeys();
			const vs = this._rankReward.getValues();
			const len = ks.length;
			byte.writeUint16(len);
			for (let i = 0; i < len; i++) {
				byte.writeLongNumber(ks[i]);
				vs[i].toBytes(byte);
			}
		}
		byte.writeBool(this._rankJoined);
		byte.writeUint32(this._maxRankIndex);
	}


	/**反序列化-二进制*/
	public fromBytes(byte: Byte): this {
		this.resetProperty();
		if (!byte) {
			return;
		}
		this._fightTimes = byte.readUint16();
		this._maxScore = byte.readLongNumber();
		this._timeStamp = byte.readLongNumber();
		this._historyScore = byte.readLongNumber();
		//排名奖励<时间，奖励内容>
		{
			const len = byte.readUint16();
			for (let i = 0; i < len; i++) {
				const k = byte.readLongNumber();
				const v = LegionBossRankReward.create();
				v.fromBytes(byte);
				this._rankReward.put(k, v);
			}
		}
		this._rankJoined = byte.readBool();
		this._maxRankIndex = byte.readUint32();
		return this;
	}


	/**克隆*/
	public clone(): LegionBoss {
		const byte = Byte.createFromPool();
		this.toBytes(byte);
		byte.pos = 0;
		const clonev = LegionBoss.create().fromBytes(byte);
		Byte.recoverToPool(byte);
		return clonev;
	}


	/**处理更新*/
	public processUpdate(jsonv: string): void {
		if (!jsonv) {
			return;
		}

		const eventID = LegionBoss.EVENT_ID;
		const us = JSON.parse(jsonv);

		for (let i = 0, len = us.length; i < len;) {
			const idx = us[i];
			//今日挑战次数
			if (idx === E_LegionBoss.FIGHT_TIMES) {
				const oldv = this._fightTimes;
				this._fightTimes = us[i + 1];
				IAsync.dispatch(eventID, idx, this._fightTimes, oldv);
				i += 2;
				continue;
			}
			//今日历史最大伤害
			if (idx === E_LegionBoss.MAX_SCORE) {
				const oldv = this._maxScore;
				this._maxScore = us[i + 1];
				IAsync.dispatch(eventID, idx, this._maxScore, oldv);
				i += 2;
				continue;
			}
			//当前记录的开始时间戳（本地时间每日0点）
			if (idx === E_LegionBoss.TIME_STAMP) {
				const oldv = this._timeStamp;
				this._timeStamp = us[i + 1];
				IAsync.dispatch(eventID, idx, this._timeStamp, oldv);
				i += 2;
				continue;
			}
			//历史分数
			if (idx === E_LegionBoss.HISTORY_SCORE) {
				const oldv = this._historyScore;
				this._historyScore = us[i + 1];
				IAsync.dispatch(eventID, idx, this._historyScore, oldv);
				i += 2;
				continue;
			}

			//排名奖励<时间，奖励内容>
			if (idx === E_LegionBoss.RANK_REWARD) {
				const k: number = us[i + 1];
				const ctrl: number = us[i + 2];
				if (ctrl === 0) {
					const oldv = this.removeRankReward(k);
					if (oldv) {
						oldv.destroy()
					}
				}
				else if (ctrl === 1) {
					const newv = LegionBossRankReward.create(us[i + 3]);
					const oldv = this.putRankReward(k, newv);
					if (oldv) {
						oldv.destroy()
					}
				}
				else {
					const curv = this.getRankReward(k);
					if (curv) {
						curv.processUpdate(us[i + 3]);
					}
					else {
						console.error('LegionBoss LegionBossRankReward is not exist when update! k = ' + k);
					}
				}
				IAsync.dispatch(eventID, idx, k, ctrl);
				i += ctrl === 0 ? 3 : 4;
				continue;
			}
			//历史上打军团boss参与排行过
			if (idx === E_LegionBoss.RANK_JOINED) {
				const oldv = this._rankJoined;
				this._rankJoined = us[i + 1];
				IAsync.dispatch(eventID, idx, this._rankJoined, oldv);
				i += 2;
				continue;
			}
			//历史最高名次 0-没用参与过，>= 1名次
			if (idx === E_LegionBoss.MAX_RANK_INDEX) {
				const oldv = this._maxRankIndex;
				this._maxRankIndex = us[i + 1];
				IAsync.dispatch(eventID, idx, this._maxRankIndex, oldv);
				i += 2;
				continue;
			}

			console.error('[Struct Data] can not find enum in LegionBoss with idx = ' + idx + ', version maybe wrong.')
		}

		//全更事件
		ITimer.callLater(this, this.dispatchTotalUpdate);
	}


	/**比对不一致结果*/
	public getCompareInfo(other: LegionBoss): string {
		if (!other) {
			return null;
		}

		const updates: any[] = [];

		if (this._fightTimes !== other._fightTimes) {
			updates.push(E_LegionBoss.FIGHT_TIMES, this._fightTimes);
		}
		if (this._maxScore !== other._maxScore) {
			updates.push(E_LegionBoss.MAX_SCORE, this._maxScore);
		}
		if (this._timeStamp !== other._timeStamp) {
			updates.push(E_LegionBoss.TIME_STAMP, this._timeStamp);
		}
		if (this._historyScore !== other._historyScore) {
			updates.push(E_LegionBoss.HISTORY_SCORE, this._historyScore);
		}

		//排名奖励<时间，奖励内容>
		{
			const idx = E_LegionBoss.RANK_REWARD;
			const ks1 = this._rankReward.getKeys();
			const ks2 = other._rankReward.getKeys();
			const rs = MathUtils.intersectSortedArray(ks1, ks2);
			if (rs[0].length > 0) {
				const list = rs[0];
				for (let i = 0, len = list.length; i < len; i++) {
					const k = list[i];
					const newv = this.getRankReward(k);
					const oldv = other.getRankReward(k);
					const ui = newv.getCompareInfo(oldv);
					if (ui && ui.length > 0) {
						updates.push(idx, k, 2, ui);
					}
				}
			}
			if (rs[1].length > 0) {
				const list = rs[1];
				for (let i = 0, len = list.length; i < len; i++) {
					const k = list[i];
					const newv = this.getRankReward(k);
					updates.push(idx, k, 1, newv.stringify(false));
				}
			}
			if (rs[2].length > 0) {
				const list = rs[2];
				for (let i = 0, len = list.length; i < len; i++) {
					updates.push(idx, list[i], 0);
				}
			}
		}
		if (this._rankJoined !== other._rankJoined) {
			updates.push(E_LegionBoss.RANK_JOINED, this._rankJoined);
		}
		if (this._maxRankIndex !== other._maxRankIndex) {
			updates.push(E_LegionBoss.MAX_RANK_INDEX, this._maxRankIndex);
		}

		const jsonv = updates.length === 0 ? null : JSON.stringify(updates);
		return jsonv;
	}


	/**全部更新*/
	protected dispatchTotalUpdate(): void {
		IAsync.dispatch(LegionBoss.TOTAL_EVENT_ID, this.getV(0));
	}

}
